#define BOOST_TEST_MODULE testGetElements

#include <memory>
#include <thread>

#include <boost/test/unit_test.hpp>

#include <crow.h>

#include "common/api/client.h"
#include "common/api/result.hpp"
#include "common/api/server.h"
#include "common/api/server_helper.hpp"

#include "common/elements/element_type.hpp"
#include "common/elements/item.h"
#include "common/elements/user.h"

#include "common/more/uuid.h"

class Server final
{
public:
    Server()
        : m_crowApplication(new crow::SimpleApp)
    {
        ADD_CROW_ROUTE_GET_ELEMENTS(
            (*m_crowApplication),
            [this](
                const Uuids::Uuid& sessionUuid,
                Elements::ElementType type,
                const std::vector<unsigned>& ids
            ) -> std::vector<std::shared_ptr<Elements::Element>>
            {
                if (sessionUuid.is_nil())
                    return {};

                std::vector<std::shared_ptr<Elements::Element>> result;
                switch (type)
                {
                case Elements::ElementType::User:
                    for (auto id : ids)
                    {
                        result.push_back(std::make_shared<Elements::User>());
                    }
                    break;

                case Elements::ElementType::Item:
                    for (auto id : ids)
                    {
                        result.push_back(std::make_shared<Elements::Item>());
                    }
                    break;
                default:
                    break;
                }
                return result;
            }
        );
        m_runThread.reset(new std::thread(
            [this]()
            { m_crowApplication->port(8080).multithreaded().run(); }
        ));
        m_stopThread.reset(new std::thread(
            [this]()
            { stop(); }
        ));
    }

    ~Server()
    {
        m_stopThread->join();
        m_runThread->join();
    }

private:
    std::unique_ptr<crow::SimpleApp> m_crowApplication;
    std::unique_ptr<std::thread> m_runThread;
    std::unique_ptr<std::thread> m_stopThread;

    void stop()
    {
        std::this_thread::sleep_for(std::chrono::seconds(2));
        m_crowApplication->stop();
    }
};

Server server;
Api::Client client("http://127.0.0.1:8080");

BOOST_AUTO_TEST_CASE(testCorrectGetElementsUsers)
{
    const auto uuid = Uuids::newUuid();
    const auto type = Elements::ElementType::User;
    const auto result = client.getElements(uuid, type, {1, 2, 3});
    BOOST_CHECK(3 == result.size());
    for (auto item : result)
        BOOST_CHECK(Elements::ElementType::User == item->elementType());
}

BOOST_AUTO_TEST_CASE(testCorrectGetElementsItems)
{
    const auto uuid = Uuids::newUuid();
    const auto type = Elements::ElementType::Item;
    const auto result = client.getElements(uuid, type, {1});
    BOOST_CHECK(1 == result.size());
    for (auto item : result)
        BOOST_CHECK(Elements::ElementType::Item == item->elementType());
}

BOOST_AUTO_TEST_CASE(testNonCorrectGetElements)
{
    const auto uuid = Uuids::newUuid();
    const auto type = Elements::ElementType::Item;
    const auto result = client.getElements(uuid, type, {});
    BOOST_CHECK(0 == result.size());
}
